perm filename GRAPHS.PAL[HAL,HE]14 blob
sn#210438 filedate 1976-04-13 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00013 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 Data structures, GSINIT
C00006 00003 UNLINK, NEWCEL
C00008 00004 NXTTIM
C00009 00005 INVLDT, INVLR0
C00011 00006 CHANGE, CHNGER
C00017 00007 ADDCHG
C00019 00008 GETVAL, GETVR0
C00021 00009 EVALND, EVLEXP
C00029 00010 MAKEXP, ADDCLC, REMCLC, DELEXP
C00036 00011 MAKEVN, DELVN
C00043 00012 MGNDS marking method for gnodes
C00057 00013 Known Bugs
C00060 ENDMK
C⊗;
; Data structures, GSINIT
.SBTTL Graph routines.
;Graph structure definitions
;RHT 9/74 RF 6/75, 10/75
COMMENT ⊗
This is the runtime's prime evil,
The murderous graph nodes and interlocks.
⊗
;GRAPH NODES ;Common fields for Variables and Expressions
II==0
XX GNMODE ;Mode bits. 1:variable. 2:expression
XX NXTGN ;Links all graph nodes. Points to next one.
XX PRVGN ;Previous link in that chain
XX INVMRK ;0 => valid, other => invalid
XX GNVAL ;points at the value cell
XX GNDEPS ;list of dependents (variables or expressions)
GNEND==II ;marks the end of the common region
;VARIABLE NODES ;Explicitly released, formed from large block store.
II==GNEND
XX VALIDF ;a count which is incremented at every reevaluation
XX VNCLCS ;list of expression nodes used as calculators
XX VNCHGS ;list of change cells. (see format below)
VNDSIZ == II/2 ;Length of variable node (in words)
;EXPRESSION NODES ;Explicitly released, formed from large block store.
II==GNEND
XX ENISB ;the ISB for this expression
XX ENIPC ;the IPC for this expression
XX ENNEED ;list of needed nodes (cell-linked)
ENDSIZ == II/2 ;Length of expression node (in words)
;CELL LINKS
II==0
XX CAR
XX CDR
;CHANGER CELL ;Explicitly released, formed from large block store.
II==0
XX NXTCHG ;next changer cell in chain
XX CHGISB ;Points to interpreter status block to resolve addressing
XX CHGIPC ;the interpeter PC where the calculation starts
CHGCSZ == II/2 ;Size of changer cell, in words
GNODES: .BLKW 1 ;head of chain of graph nodes.
TIME: 0 ;used during evaluation of nodes
VALIDNO:0 ;used for validity field of nodes
GNEVT: .BLKW 1 ;event for interlocking graph references
GSINIT:
;Initialize the graph structure to a null situation;
EVMAK ;Make a new interlock event.
MOV (SP),GNEVT;
EVSIG ;Give it one signal.
CLR GNODES;
CLR TIME;
CLR VALIDNO;
RTS PC ;Done
; UNLINK, NEWCEL
UNLINK:
COMMENT ⊗ A list is in R0, and an entity in R1. All occurrences of
that entity, if any, are removed from the list. The orphaned cells
are left for the garbage collector. A pointer to the list is
returned in R0 (it has changed if the first element was deleted). ⊗
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV R0,R2 ;R2 ← forward pointer
BEQ UNL4 ;If no list, then done
MOV R1,-(SP) ;Save R1
JSR PC,NEWCEL ;R0 ← dummy header
MOV (SP)+,R1 ;Restore R1
MOV R2,CDR(R0) ;Set up dummy header
MOV R0,R3 ;R3 ← backward pointer
UNL2: CMP CAR(R2),R1 ;Match?
BEQ UNL1 ;Yes
MOV R2,R3 ;
UNL3: MOV CDR(R2),R2 ;Move along
BNE UNL2 ;If any more
MOV CDR(R0),R0 ;Go past dummy
UNL4: MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
RTS PC ;Return
UNL1: MOV CDR(R2),CDR(R3) ;Link past the orphan
BR UNL3 ;Continue
NEWCEL:
COMMENT ⊗ Returns in R0 a pointer at a cell. Taken from cell space
unless there is none. Uses direct jumps, lets the subsidiary do the
return. ⊗
.IFNZ SMALLB
MOV #CELSPC,R0 ;
JMP GETSBK ;Allocate from small blocks
.IFF
MOV #2,R0 ;Number of words needed
JMP GTFREE ;R0 ← LOC[new block]
.ENDC
; NXTTIM
COMMENT ⊗
JSR PC,NXTTIM
Returns TIME←TIME+1 in R0. If TIME goes negative then set all
positive mark cells to negative, then set time to 1. ⊗
NXTTIM: INC TIME ;TIME←TIME+1
MOV TIME,R0
BGT NXT.RT ;OK?
MOV GNODES,R0 ;
BEQ NXTT.3 ;DID WE HAVE ANY??
NXTT.1: TST INVMRK(R0) ;YES
BLE NXTT.2 ;WAS INVMRK POSITIVE
NEG INVMRK(R0) ;YES, NEGATE IT
NXTT.2: MOV NXTGN(R0),R0 ;GO ON TO NEXT
BNE NXTT.1 ;IF ANY
NXTT.3: INC R0 ;R0←0+1
MOV R0,TIME ;TIME IS 1 AGAIN
NXT.RT: RTS PC
; INVLDT, INVLR0
INVLDT:
COMMENT ⊗ Called only from the outside world. R0 is the node to
invalidate, along with all dependents. We must invalidate dependents
even if the given node is already invalid, unless we have just now
invalidated it, which would imply that we are in a cycle. ⊗
EVWAIT GNEVT ;We change TIME, so must lock this
MOV R0,R1
JSR PC,NXTTIM
MOV R1,R0
JSR PC,INVLR0
EVSIG GNEVT ;End of critical section
RTS PC
INVLR0: CMP INVMRK(R0),TIME ;Are we in a cycle?
BEQ INVL.R ;Yes. Return.
INVL.1: MOV TIME,INVMRK(R0) ;No. Invalidate this node.
MOV R2,-(SP) ;Save R2 for recursive call
MOV GNDEPS(R0),R2 ;R2 ← list of dependents
BEQ INVL.X ;If any
INVL.2: MOV CAR(R2),R0 ;R0 ← next dependent
JSR PC,INVLR0 ;Go Invalidate it.
MOV CDR(R2),R2 ;Repeat for the rest
BNE INVL.2 ;If any
INVL.X: MOV (SP)+,R2 ;Restore R2
INVL.R: RTS PC
; CHANGE, CHNGER
COMMENT ⊗ Called by the outside world to put a new value, CHG.VNEW,
in the variable node CHG.ND. Returns with CHG.ND in R0. ⊗
ROUTINE CHANGE,<CHG.ND,CHG.VNEW>
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV CHG.ND(RF),R1 ;R1 ← the target node.
EVWAIT GNEVT ;Wait until OK to enter critical code.
JSR PC,NXTTIM ;
MOV R1,R0 ;
JSR PC,INVLR0 ;invalidate it for the nonce
MOV CHG.ND(RF),R0 ;R0 ← the target node
MOV GNVAL(R0),R2 ;R2 ← old value
MOV CHG.VNEW(RF),GNVAL(R0) ;stow the new value
MOV VNCHGS(R0),R3 ;R3 ← list of changers
BEQ CH.XXX ;if any
EVSIG GNEVT ;Leave the overall graph node critical region.
CH.1: JSR PC,CHNGER ;Call the next change routine
MOV NXTCHG(R3),R3 ;R3 ← next changer
BNE CH.1
EVWAIT GNEVT ;Enter critical section again.
CH.XXX: MOV CHG.ND(RF),R0 ;R0 ← the target node
CLR INVMRK(R0) ;Revalidate it
INC VALIDNO ;A new validity number
MOV VALIDNO,VALIDF(R0) ;which is put in the validf
EVSIG GNEVT ;Ok for others to enter critical code now.
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
RTS RF ;Return
CHNGER:
COMMENT ⊗ Calls the change routine indicated. This is done by
instantiating a new interpreter to do the work. It should terminate
the normal way, with a TERMINATE command. R2 points to the old
value, and CHG.VNEW(RF) points to the new value. R3 points to the
changer cell. These values are put into the new ISB. GNODE
exclusion should be released before the call to CHNGER. Recall that
a changer cell looks like this:
XX NXTCHG ;next changer cell in chain
XX CHGISB ;Points to interpreter status block to resolve addressing
XX CHGIPC ;the interpeter PC where the calculation starts
⊗
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV R4,-(SP) ;Save R4
;make a new interpreter to do the work
MOV CHGISB(R3),R4 ;R4 ← ISB we have to emulate
MOV CHGIPC(R3),R0 ;R0 ← IPC of new ISB
EVMAK ;Stack a new event for communication with subsidiary
MOV (SP),R1 ;R1 ← copy of that event
JSR PC,SPAWN ;R0 ← Process decriptor
MOV PDBR4(R0),R4;R4 ← ISB of new interpreter
MOV R2,OLDV(R4) ;Stow the "old value" pointer in environment.
MOV CHG.VNEW(RF),NEWV(R4) ;Stow the "new value" pointer.
FORK R0,#INTERP,#2 ;Cause the new process to be started at high prio.
;clean up after the interpreter
EVWAIT ;Wait for the completion event (still on stack)
MOV (SP)+,R4 ;Restore R4
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
RTS PC ;Done
; ADDCHG
ROUTINE ADDCHG,<ACH.ND,ACH.CHG>
COMMENT ⊗ ACH.ND is the target graph node, and ACH.CHG is a changer
cell all prepared except for the link to the other changers. It is
necessary to perform this linking. ⊗
MOV R2,-(SP) ;Save R2
MOV ACH.ND(RF),R2 ;R2 ← LOC[target node]
MOV ACH.CHG(RF),R1 ;R1 ← LOC[changer cell]
EVWAIT GNEVT ;Enter critical region for graph nodes
MOV VNCHGS(R2),NXTCHG(R1) ;Link new changer into list
MOV R1,VNCHGS(R2) ;
EVSIG GNEVT ;Leave critical region
MOV (SP)+,R2 ;Restore R2
RTS R5 ;Done
; GETVAL, GETVR0
COMMENT ⊗ Called by the outside world. Returns LOC[value(GTV.ND)] in
R0 and the VALIDF in R1, after having scrounged around to get a valid
value, if necessary and possible. ⊗
ROUTINE GETVAL,<GTV.ND>
MOV GTV.ND(RF),R0
JSR PC,GETVR0
RTS RF
GETVR0: TST INVMRK(R0) ;Is the current value good?
BEQ GETV.R ;Yes
EVWAIT GNEVT ;No. Enter critical region.
MOV R0,-(SP) ;Stack R0
MOV RF,-(SP)
MOV R0,-(SP) ;EVALNODE(GTV.ND,TIME←TIME+1)
JSR PC,NXTTIM
MOV R0,-(SP)
MOV #MARK2,-(SP)
MOV SP,RF
JSR PC,EVALND
EVSIG GNEVT ;Leave critical region
MOV (SP)+,R0 ;R0 ← target node, now validated
GETV.R: MOV VALIDF(R0),R1 ;Get the validity count
MOV GNVAL(R0),R0 ;R0 ← value cell
RTS PC ;Done
; EVALND, EVLEXP
COMMENT ⊗ EVALND is a recursive procedure, which is given EVL.ND, the
target node to evaluate, and EVL.T, the "time" of evaluation. If
necessary, it calls itself at the same "time" to track down a chain
of related nodes. GNEVT exclusion should be on before this routine
is first called, and will remain on after the return. ⊗
ROUTINE EVALND,<EVL.ND,EVL.T>
MOV EVL.ND(RF),R0 ;R0 ← target graph node
MOV INVMRK(R0),R1 ;Is the node already valid?
BEQ EV.RTS ;Yes
CMP R1,EVL.T(RF) ;No. Have we already looked at it this "time"?
BEQ EV.RTS ;Yes
MOV EVL.T(RF),INVMRK(R0) ;No. We have touched our node now
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
BIT #1,GNMODE(R0) ;A variable or an expression?
BNE EV.TM1 ;Variable
CALL EVLEXP,<R0,EVL.T> ;Sets GNVAL and INVMRK correctly
BR EV.XXX ;Done
EV.TM1: ;evaluate a variable.
MOV VNCLCS(R0),R2 ;R2 ← list of calculator expressions
BEQ EV.XXX ;if any
EV.TM4: MOV CAR(R2),R1 ;R1 ← first expression
TST INVMRK(R1) ;Is this expression valid?
BNE EV.TM3 ;No
MOV GNVAL(R1),GNVAL(R0) ;Yes. Copy its value pointer
BR EV.SUC ;Success exit.
EV.TM3: MOV CDR(R2),R2 ;R2 ← rest of expression list
BNE EV.TM4 ;Try with next one.
;no currently valid expression. Try to evaluate one.
MOV VNCLCS(R0),R2 ;R2 ← list of calculator expressions
BEQ EV.XXX ;if any
MOV CAR(R2),R1 ;R1 ← first expression
EV.TM7: CALL EVALND,<R1,EVL.T(RF)>
MOV CAR(R2),R1 ;
TST INVMRK(R1) ;Successfully evaluated?
BEQ EV.TM6 ;Yes
MOV CDR(R2),R2 ;Try next one
BNE EV.TM7 ;If any
BR EV.XXX ;Give up
EV.TM6: MOV EVL.ND(RF),R0 ;
MOV GNVAL(R1),GNVAL(R0) ;Transfer the value
BR EV.SUC ;Success return
;all the needs are met for the expression in CAR(R2)
MOV CAR(R2),R1 ;R1 ← expression node
EV.NOK: CALL EVALND,<R1,EVL.T(RF)> ;Evaluate the expression.
MOV EVL.ND(RF),R0 ;R0 ← target node
MOV GNVAL(R1),GNVAL(R0) ;Stow away its new value.
EV.SUC: CLR INVMRK(R0) ;Mark it as valid.
INC VALIDF(R0) ;Increment its validity number
EV.XXX: MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
EV.RTS: RTS RF ;Done
COMMENT ⊗ Each expression has a field, ENISB, which points to the
interpreter status block of its definition. This contains enough
information to resolve any variable references in the expression.
Calls the interpreter in a special way (through a CALL INTERP) having
first set up a pseudo-ISB in R4. When the interpreter returns, the
desired value should be in R0. Each time called, this routine
constructs a new pseudo-ISB, uses it once, and then releases it.
This is somewhat wasteful, and could be cleaned up. The value found
is put in the expression node, which is marked as valid. It is not
assumed that all the needed nodes have been validated before this
routine is called. ⊗
ROUTINE EVLEXP,<EVE.EXP,EVE.T>
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV R4,-(SP) ;Save R4
;try to validate the needed list
MOV EVE.EXP(RF),R0 ;R0 ← LOC[ENODE]
MOV EVE.T(RF),INVMRK(R0) ;We are looking now.
MOV ENNEED(R0),R3 ;R3 ← needed list
BEQ EVE.L2 ;if any
EVE.L3: CALL EVALND,<CAR(R3),EVL.T(RF)> ;Evaluate this need.
MOV CAR(R3),R0 ;R0 ← variable node of the need
TST INVMRK(R0) ;Is it now valid?
BNE EVE.FF ;No, so we fail
MOV CDR(R3),R3 ;Yes. R3 ← next needed cell
BNE EVE.L3 ;If any.
;the needed list is ready
EVE.L2: MOV #ISBS,R0 ;Get a pseudo-ISB
JSR PC,GTFREE ;R0 ← LOC[new ISB]
MOV R0,R4 ;R4 ← LOC[new ISB]
MOV EVE.EXP(RF),R2 ;
MOV ENISB(R2),R1 ;R1 ← LOC[old ISB]
MOV ENV(R1),ENV(R4) ;Copy environments
MOV LEV(R1),LEV(R4) ;Copy levels
MOV ENIPC(R2),IPC(R4) ;Initialize the IPC
MOV #INSTSZ,R0 ;
JSR PC,GTFREE ;R0 ← LOC[new interpreter stack]
MOV R0,-(SP) ;Save the stack location
ADD #INSTSZ,R0 ;
MOV R0,R3 ;R3 ← LOC[verge of new interpreter stack]
INC GCOK ;Don't garbage collect during this.
CALL INTERP ;Enter the interpreter, R0 ← LOC[new value cell]
DEC GCOK ;Garbage collect ok now.
MOV R0,R2 ;R2 ← LOC[new value cell]
MOV R4,R0 ;Release the ISB
JSR PC,RLFREE ;
MOV (SP)+,R0 ;Release the interpreter stack
JSR PC,RLFREE ;
MOV EVE.EXP(RF),R0 ;R0 ← LOC[expression node]
CLR INVMRK(R0) ;Now valid
MOV R2,GNVAL(R0);Result into the node
EVE.FF: MOV (SP)+,R4 ;Restore R4
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
RTS RF ;Return
; MAKEXP, ADDCLC, REMCLC, DELEXP
ROUTINE ADDCLC,<ADD.VN,ADD.EN>
COMMENT ⊗ ADD.VN is the variable node, and ADD.EN is an expression
node all prepared except for the link to the variable node. This
linking is performed. ⊗
⊗
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV ADD.VN(RF),R2 ;R2 ← LOC[variable node]
MOV ADD.EN(RF),R3 ;R3 ← LOC[expression node]
EVWAIT GNEVT ;Enter critical region
;Add the VNODE as a dependent of the ENODE
JSR PC,NEWCEL ;R0 ← LOC[new cell]
MOV GNDEPS(R3),CDR(R0)
MOV R2,CAR(R0) ;
MOV R0,GNDEPS(R3) ;
;Add the ENODE as an expression of the VNODE
JSR PC,NEWCEL ;R0 ← LOC[new cell]
MOV VNCLCS(R2),CDR(R0)
MOV R3,CAR(R0) ;
MOV R0,VNCLCS(R2) ;
EVSIG GNEVT ;Leave critical region
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
RTS RF ;Done
ROUTINE MAKEXP,<MKE.ISB,MKE.IPC,MKE.NDS>
COMMENT ⊗ Makes a new expression node with ENISB, ENIPC, ENNDS as
specified. Makes it a dependent of all the variables on the needed
list. ⊗
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV #ENDSIZ,R0 ;
JSR PC,GTFREE ;
MOV R0,R3 ;R3 ← LOC[new expression node]
MOV #2,GNMODE(R3) ;Expression
MOV #-1,INVMRK(R3) ;Invalid
CLR GNVAL(R3) ;No value
CLR GNDEPS(R3) ;No variable dependents
MOV MKE.ISB(RF),ENISB(R3) ;ISB
MOV MKE.IPC(RF),ENIPC(R3) ;IPC
MOV MKE.NDS(RF),R2 ;Need list
EVWAIT GNEVT ;Enter critical region
MOV R2,ENNEED(R3) ;
;this expression is a dependent for each variable on the needed list.
BEQ MKE2 ;If any
MKE1: JSR PC,NEWCEL ;
MOV CAR(R2),R1 ;R1 ← the next needed variable
MOV GNDEPS(R1),CDR(R0) ;
MOV R3,CAR(R0) ;
MOV R0,GNDEPS(R1)
MOV CDR(R2),R2 ;
BNE MKE1 ;Repeat
MKE2: ;link up with all other graph nodes in the world
MOV GNODES,R1 ;
BEQ MKE3 ;If any
MOV R1,NXTGN(R3) ;
MOV R3,PRVGN(R1) ;
MKE3: MOV R3,GNODES ;
MOV R3,R0 ;R0 ← LOC[new expression node]
EVSIG GNEVT ;Leave critical region
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
RTS RF ;Done
ROUTINE REMCLC,<RMC.VN,RMC.EN>
COMMENT ⊗ Unlinks the given expression from the given variable. ⊗
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV RMC.EN(RF),R2 ;R2 ← ENODE
MOV RMC.VN(RF),R3 ;R3 ← VNODE
CALL GETVAL,<R3> ;Make sure he has a last chance to get value.
EVSIG GNEVT ;Enter critical region
;remove the VNODE as a dependent of the ENODE
MOV GNDEPS(R2),R0 ;
MOV R3,R1 ;
JSR PC,UNLINK ;
MOV R0,GNDEPS(R2) ;
;remove the ENODE as a calculator of the VNODE
MOV VNCLCS(R3),R0 ;
MOV R2,R1 ;
JSR PC,UNLINK ;
MOV R0,VNCLCS(R3) ;
EVSIG GNEVT ;Leave critical region
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
RTS RF ;Done
ROUTINE DELEXP,<DLE.EN>
COMMENT ⊗ Must remove this expression from all variable nodes which
are dependent on it, having tried to validate them. Then unlink the
expression node and reclaim it. ⊗
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV DLE.EN(RF),R2 ;R2 ← LOC[victim expression node]
EVWAIT GNEVT ;Enter critical region
MOV GNDEPS(R2),R3 ;R3 ← list of dependents
BEQ DLE1 ;If any
JSR PC,NXTTIM ;
MOV R0,-(SP) ;New time for the evaluations to follow
DLE2: MOV CAR(R3),R0 ;
MOV (SP),R1 ;Time
CALL EVALND,<R0,R1> ;Try to validate him
MOV VNCLCS(R0),R0 ;R0 ← his list of calculators
MOV R2,R1 ;us
JSR PC,UNLINK ;Remove us from that list
MOV CAR(R3),R1 ;him
MOV R0,VNCLCS(R1) ;Put back his calculator list, minus us.
MOV CDR(R3),R3 ;Next calculator
BNE DLE2 ;If any
TST (SP)+ ;Clear the time from the stack
;unlink this expression node
DLE1: MOV NXTGN(R2),R1 ;R1 ← forward link
MOV PRVGN(R2),R0 ;R0 ← backward link
MOV R0,PRVGN(R1) ;
MOV R1,NXTGN(R0) ;
EVSIG GNEVT ;Leave critical region.
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
RTS RF ;Done
; MAKEVN, DELVN
MAKEVN:
COMMENT ⊗ Creates a variable node, with no frills (no calculators,
changers, loksh, boydem, tsibele, ...) except that if R0 is
non-zero, that is assumed to be the value cell pointer. The node
will be marked as invalid unless there was some value given. The
space is taken from large block storage. The new variable node is
returned in R0. ⊗
MOV R0,-(SP) ;Save R0
MOV #VNDSIZ,R0 ;
JSR PC,GTFREE ;R0 ← LOC[new graph node]
CLR INVMRK(R0) ;Validate the node
MOV #1,GNMODE(R0) ;Variable, not expression
MOV (SP)+,GNVAL(R0) ;Stuff away the value cell pointer.
BNE MKVN2 ;Was there one?
MOV #-1,INVMRK(R0) ;No. Invalidate this node.
MKVN2: CLR GNDEPS(R0) ;Zero other fields
CLR VNCLCS(R0) ;
CLR VNCHGS(R0) ;
CLR NXTGN(R0) ;
CLR PRVGN(R0) ;
CLR VALIDF(R0) ;
EVWAIT GNEVT ;Critical section here
MOV GNODES,R1 ;Link up to other nodes in the world.
BEQ MKVN1 ;If any
MOV R1,NXTGN(R0);
MOV R0,PRVGN(R1);
MKVN1: MOV R0,GNODES ;
EVSIG GNEVT ;End of critical section
RTS PC ;
DELVN:
COMMENT ⊗ R0 is the location of the variable node. All dependent
expressions are first validated if possible, then those are deleted.
The value cell and all cell lists (like GNDEPS, VNCLCS) are reclaimed
by relying on the garbage colector. Thus graph nodes may share value
cells. The changer list is explicitly released, so changer lists may
not be shared. Then the node itself is unlinked from the chain and
returned to free storage. ⊗
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV R0,R2 ;R2 ← variable node to delete
;Try to validate the dependents
MOV GNDEPS(R2),R3 ;R3 ← List of dependent expressions
BEQ DEL3 ;if any
JSR PC,NXTTIME ;R0 ← next "time"
MOV R0,-(SP) ;Save "time"
DEL2: MOV (SP),R0 ;Validate all dependents with this "time".
CALL EVALND,<CAR(R3),R0>;Evaluate one expression
CALL DELEXP,<CAR(R3)> ;Delete expression.
MOV CDR(R3),R3 ;R3 ← next dependent
BNE DEL2 ;if any
TST (SP)+ ;clean time off stack
;Tell each calculator expression that we are no longer dependent
DEL3: EVWAIT GNEVT ;Enter critical region
MOV VNCLCS(R2),R3 ;R3 ← List of calculator expressions
BEQ DEL4 ;If any
DEL5: MOV CAR(R3),R0 ;
MOV GNDEPS(R0),R0 ;R0 ← that expression's dependent list
MOV R2,R1 ;R1 ← us
JSR PC,UNLINK ;Remove us from his dependent list
MOV CAR(R3),R1 ;R1 ← him
MOV R0,GNDEPS(R1) ;Replace his new dependent list
MOV CDR(R3),R3 ;Do the same for the other calculator expressions
BNE DEL5 ;If any
;Reclaim the changer cells
DEL4: MOV VNCHGS(R2),R3 ;R3 ← First changer cell
DEL6:: MOV R3,R0 ;R0 ← current changer cell
BEQ DEL7 ;If any
MOV NXTCHG(R3),R3 ;R3 ← next changer cell
JSR PC,RLFREE ;Release current one
BR DEL6 ;Do the others
;Unlink this graph node
DEL7: MOV NXTGN(R2),R1 ;R1 ← forward link
MOV PRVGN(R2),R0 ;R0 ← backward link
BEQ DEL8 ;if any
MOV R1,NXTGN(R0) ;
BR DEL9 ;
DEL8:: MOV R1,GNODES ;if no backward link, then the header.
DEL9: TST R1 ;Is there a forward link?
BEQ DEL10 ;No
MOV R0,PRVGN(R1) ;Yes. set up next guy's backpointer
DEL10: EVSIG GNEVT ;Leave critical region.
MOV R2,R0 ;R0 ← target graph node hulk
JSR PC,RLFREE ;Release it.
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
RTS PC ;Done
; MGNDS marking method for gnodes
MGNDS: ;Marking method for GNODES
MOV R2,-(SP) ;Save R2
EVWAIT GNEVT ;Enter critical region
MOV GNODES,R2 ;R2 ← LOC[first graph node]
BEQ MGNDS1 ;If none, then done
MGNDS2: MOV GNVAL(R2),R0 ;Mark the value cell
JSR PC,MARKQ ;
MOV R0,GNVAL(R2) ;Put it back (compactification may move it)
MOV GNDEPS(R2),R0 ;Mark the cells used in the dependent list
JSR PC,MCELL ;
MOV R0,GNDEPS(R2) ;Put it back
BIT #1,GNMODE(R2) ;What kind of graph node is it?
BEQ MGNDS4 ;
MOV VNCLCS(R2),R0 ;A variable. Mark cells in CLC list
JSR PC,MCELL ;
MOV R0,VNCLCS(R2) ;Put it back
BR MGNDS3 ;
MGNDS4: MOV ENNEED(R2),R0 ;An expression. Mark cells in ENNEED list
JSR PC,MCELL ;
MOV R0,ENNEED(R2) ;Put it back
MGNDS3: MOV NXTGN(R2),R2 ;R2 ← LOC[next graph node]
BNE MGNDS2 ;Repeat as necessary
MGNDS1: MOV (SP)+,R2 ;Restore R2
EVSIG GNEVT ;Leave critical region
RTS RF ;Return
; Known Bugs
COMMENT ⊗ It is possible that while a graph node is changed, a
changer is invoked. During its execution, some other process
modifies the change list for that node. When the changer is done, it
may get lost in the changer cell list. Graph node exclusion must be
turned off during execution of a changer, so that it can change other
cells. Special changer exclusion causes deadlock in the case that
one changer triggers another.
Certain variables, like YELLOW and BLUE, are not being initialized
to anything.
When a variable goes away, calculators depending on it are
not being destroyed, even though they are unlinked
from anyone who uses them.
⊗